#include <windows.h>

#include "lib.h"
#include "npipe.h"

NamedPipe::NamedPipe()
{
	hPipe = INVALID_HANDLE_VALUE;
	iused = oused = 0;
	state = BADINIT;
	over = new OVERLAPPED;

	if (!over)
		return;
	ZeroMemory(over, sizeof(OVERLAPPED));

	over->hEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
	if (!over->hEvent)
		return;
}

NamedPipe::~NamedPipe()
{
	if (hPipe != INVALID_HANDLE_VALUE)
	{
		CloseHandle(hPipe);
		hPipe = INVALID_HANDLE_VALUE;
	}

	if (over)
	{
		if (over->hEvent)
			CloseHandle(over->hEvent);

		delete over;
	}
}

bool8
NamedPipe::create(String& Name, uint32 Timeout, void* Security)
{
	if (hPipe != INVALID_HANDLE_VALUE)
		return false;
	
	hPipe = CreateNamedPipe(Name,
		PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
		PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
		1, BUFSIZE, BUFSIZE, Timeout, (LPSECURITY_ATTRIBUTES)Security);

	return (bool8)(hPipe != INVALID_HANDLE_VALUE);
}

bool8
NamedPipe::initOk()
{
	return (bool8)(0 !=
		(over && over->hEvent && hPipe != INVALID_HANDLE_VALUE));
}

HANDLE
NamedPipe::getEvent()
{
	return over->hEvent;
}

void
NamedPipe::ready()
{
	connectToClient();
}

void
NamedPipe::connectToClient()
{
	connectionReset();

	if (ConnectNamedPipe(hPipe, over))
		postRead();
	else
	{
		switch (GetLastError()) 
		{ 
			case ERROR_IO_PENDING: 
				state = CONNECTING;
				break; 
			
			case ERROR_PIPE_CONNECTED:		// Client already connected
				postRead();
				break;
 
			default: 
				state = BADINIT;
				break;
		} 
	}
}

void
NamedPipe::closeClient()
{
	if (DisconnectNamedPipe(hPipe))
		connectToClient();
	else
		state = BADINIT;
}

bool8	
NamedPipe::processIO()
{
	BOOL success;
	DWORD xferd;

	if (hPipe == INVALID_HANDLE_VALUE)
		return false;

	success = GetOverlappedResult(hPipe, over, &xferd, FALSE);

	switch (state)
	{
		case CONNECTING: 
			if (success)
				postRead();
			else
			{
				state = BADINIT;
				return false;
			}
			break;

		case READING:
			if (success && xferd != 0)
			{
				iused += xferd;

				if (getResponse())		// process ibuf and send response
					postWrite();
				else					// partial input, read more from client
					postRead();
			}
			else
			{
				DEBUGMSG(("Connection::processIO:  READING: 0  "));
				state = BADINIT;
				closeClient();
			}
			break;

		case WRITING:
			if (success && xferd == (DWORD)oused)
			{
				oused = 0;
				postRead();			// get next request
			}
			else
			{
				DEBUGMSG(("Connection::processIO:  WRITING (%ld)  ",
					GetLastError()));
				state = BADINIT;
				closeClient();
			}
			break;

		case BADINIT:
		default:
			return false;
	}
	return true;
}

void
NamedPipe::postRead()
{
	state = READING;

	if (ReadFile(hPipe, ibuf + iused, BUFSIZE - iused, NULL, over))
		return;
	else if (GetLastError() == ERROR_IO_PENDING)
		return;
	else
	{
		state = BADINIT;
		closeClient();
	}
}

void
NamedPipe::postWrite()
{
	state = WRITING;

	if (WriteFile(hPipe, obuf, oused, NULL, over))
		return;
	else if (GetLastError() == ERROR_IO_PENDING)
		return;
	else
	{
		state = BADINIT;
		closeClient(); 
	} 
}

void
NamedPipe::connectionReset()
{
	iused = oused = 0;
	state = BADINIT;
}

bool8
NamedPipe::getResponse()
{
	if (iused)
	{
		// echo back response
		//
		CopyMemory(obuf, ibuf, iused);

		oused = iused;
		iused = 0;
	}

	return (bool8)(oused != 0);
}
